package com.neusoft.lohas.common.restTemplate.client;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.springframework.core.io.Resource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.RestTemplate;

public class ServerApi {

	private static final String CHAR_ECODE = "UTF-8";
	private final static int TIMEOUT_CONNECTION = 20000;
	private final static int TIMEOUT_SOCKET = 20000;
	private final static int RETRY_TIME = 3;

	private volatile String cookie = "userId=421";
	private volatile String userAgent;
	private final static String protocol = "http";
	// private String host = "www.buutuu.com";
	private final static String host = "www.9ievent.com";
	private final static String port = "8080";
	private final static String servicePath = "ievent";
	private final static String suffix = ".dl";
	private String serverBaseAddress;

	private static ServerApi mServerApi;

	public static ServerApi getInstance() {
		if (mServerApi == null)
			mServerApi = new ServerApi();
		return mServerApi;
	}

	private ServerApi() {
	}

	/**
	 * 对返回的头信息进行处理，主要是缓存
	 * 
	 * @param responseEntity
	 */
	private void handleResponsHeaders(HttpHeaders responseEntity) {
		if (responseEntity == null)
			return;
		List<String> cookies = responseEntity.get("Set-Cookie");
		if (cookies != null && cookies.size() > 0)
			changeCookie(cookies.get(0));
	}

	/**
	 * 更改本地cookie缓存
	 * 
	 * @param cookie
	 */
	private void changeCookie(String cookie) {
		if (StringUtils.isEmpty(cookie))
			return;

		if (this.cookie == null) {
			this.cookie = cookie;
		}
		if (this.cookie.equals(cookie)) {
			return;
			// this.cookie = cookie;
		}
		this.cookie = cookie;
		// TODO:dosomething when cookie change

	}

	/**
	 * 获取客户端代理
	 * 
	 * @param appContext
	 * @return
	 */
	private String getUserAgent() {
		if (userAgent == null || userAgent == "") {
			StringBuilder sb = new StringBuilder("9ievent.com");
			sb.append('/' + "web");
			sb.append("/" + "2.0.1");// 手机系统版本
			sb.append("/" + "aliyun"); // 手机型号
			sb.append("/" + "1");// 客户端唯一标识
			userAgent = sb.toString();
		}
		return userAgent;
	}

	/**
	 * 获取服务器路径
	 * 
	 * @return
	 */
	private String getServerBaseAddress() {
		if (serverBaseAddress == null) {
			serverBaseAddress = protocol + "://" + host + ":" + port + "/"
					+ servicePath;
		}
		return serverBaseAddress;
	}

	/**
	 * 构造资源url
	 * 
	 * @param resource
	 * @param ids
	 * @param url上的参数
	 * 
	 * @return
	 */
	private String makeUrl(String resource, Map<String, String> params,
			String... ids) {
		StringBuilder sb;
		if (ids == null || ids.length == 0) {
			if (params == null || params.size() == 0)
				return getServerBaseAddress() + "/" + resource + suffix;
		}
		sb = new StringBuilder(getServerBaseAddress() + "/" + resource);
		for (String id : ids) {
			if(id != null && !"".equals(id))
				sb.append("/" + id);
		}
		if (!StringUtils.isEmpty(resource))
			sb.append(suffix);
		boolean hasAnd = false;
		if (params != null && params.size() > 0) {
			sb.append("?");
			for (Map.Entry<String, String> kv : params.entrySet()) {
				if (hasAnd)
					sb.append('&');

				sb.append(kv.getKey());
				sb.append('=');
				try {
					sb.append(URLEncoder.encode(kv.getValue(), CHAR_ECODE));
				} catch (UnsupportedEncodingException e) {
					e.printStackTrace();// 不会发生的错误不用管它
				}
				hasAnd = true;
			}
		}

		return sb.toString();
	}

	

	/**
	 * 新建http 头信息，因为有一些公共的http头信息，所有从这里来new出来, 这个头信息是可变
	 * 
	 * @return
	 */
	private HttpHeaders getNewCommonHeader(Object upload, Class<?> returnClazz) {
		HttpHeaders headers = new HttpHeaders();
		headers.add("User-Agent", getUserAgent());
		List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>();
		// acceptableMediaTypes.add(MediaType.APPLICATION_JSON);

		if (Resource.class.isAssignableFrom(returnClazz)) {
			acceptableMediaTypes.add(MediaType.IMAGE_JPEG);
			acceptableMediaTypes.add(MediaType.IMAGE_PNG);
			acceptableMediaTypes.add(MediaType.IMAGE_GIF);
			acceptableMediaTypes.add(MediaType.APPLICATION_JSON);
		}
		headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
		headers.setAccept(acceptableMediaTypes);
		if (!StringUtils.isEmpty(cookie))
			headers.add("Cookie", cookie);
		return headers;
	}

	/**
	 * 所有网络操作的入口函数，依据rest原则， 可以知道，我们得大部分操作是相同的，并且简单的，只需要对特别情况做特殊处理就好了
	 * 
	 * @param resource
	 * @param method
	 * @param resourceEnum
	 * @param resourceId
	 * @return
	 * @throws NetworkErrorException
	 */
	public <T, B> ResourceHandleResult<B> remoteResourceHandle(T resource,
			ResourceHandleType method, ResourceEnum resourceEnum,
			Class<B> returnClazz,
			Map<String, String> params, String... resourceId)
	{

		String url = makeUrl(resourceEnum.getType(), params,
				resourceId);
		HttpHeaders headers = getNewCommonHeader(resource, returnClazz);
		// 存储form参数
		MultiValueMap<String, ?> formData = null;
		if (resource != null && (resource instanceof MultiValueMap)) {
			formData = (MultiValueMap<String, ?>) resource;
		} else {
			formData = getFormDateIfNeed(resource,
					resourceEnum);
		}
		// if(method == ResourceHandleType.POST||method ==
		// ResourceHandleType.PUT){
		// // 这两种参数都有for参数,form数据都进行 mult编码
		// headers.setContentType(MediaType.MULTIPART_FORM_DATA);
		// formData = getFormDate(resource, resourceEnum);
		// }
		// HttpEntity<MultiValueMap<String, Object>> requestEntity = new
		// HttpEntity<MultiValueMap<String, Object>>(
		// formData, headers);
		// formData = getFormDate(resource, resourceEnum);
		HttpEntity<?> requestEntity = null;
		HttpMessageConverter<?> converter = null;
		RestTemplate restTemplate = new RestTemplate();
		resetMessageConverter(restTemplate.getMessageConverters());
		if (formData != null) {
			headers.setContentType(MediaType.MULTIPART_FORM_DATA);
			requestEntity = new
					HttpEntity<MultiValueMap<String, ?>>(formData, headers);
			converter = new FormHttpMessageConverter();
			((FormHttpMessageConverter) converter).setCharset(Charset
					.forName("UTF-8"));

		}
		else {
			requestEntity = new HttpEntity<T>(resource, headers);
			converter = new MappingJackson2HttpMessageConverter();
		}
		restTemplate.getMessageConverters().add(converter);

		try {
			ResponseEntity<B> response = restTemplate
					.exchange(url, HttpMethod.values()[method.ordinal()],
							requestEntity, returnClazz);

			if (response.getStatusCode() == HttpStatus.NOT_FOUND)
				throw new IOException("网络不通");
			handleResponsHeaders(response.getHeaders());

			ResourceHandleResult<B> result = new ResourceHandleResult<B>(
					response.getBody(), response);
			return result;

		} catch (HttpClientErrorException e) {
			return new ResourceHandleResult<B>(null, e.getStatusCode().value(),
					e.getResponseBodyAsString());
		} catch (HttpServerErrorException e) {
			return new ResourceHandleResult<B>(null, e.getStatusCode().value(),
					e.getResponseBodyAsString());
		} catch (Exception e) {
			e.printStackTrace();
			return new ResourceHandleResult<B>(null, -1, "未知錯誤");
		}
	}

	private void resetMessageConverter(List<HttpMessageConverter<?>> list) {

		int index = 0;
		for (; index < list.size(); index++) {
			HttpMessageConverter<?> httpMessageConverter = list.get(index);
			if (httpMessageConverter instanceof StringHttpMessageConverter)
			{
				list.remove(index);
				list.add(index, new StringHttpMessageConverter(Charset
						.forName("UTF-8")));

			}
			if (httpMessageConverter instanceof AllEncompassingFormHttpMessageConverter) {
				AllEncompassingFormHttpMessageConverter converter = (AllEncompassingFormHttpMessageConverter) httpMessageConverter;
				converter.setPartConverters(list);
			}
		}
	}

	/**
	 * 获取返回值的转换器,返回值,有可能是json,用可能是steam
	 * 
	 * @param resource
	 * @param method
	 * @param resourceEnum
	 * @return
	 */
	private HttpMessageConverter<?> getRsponseConverter(
			ResourceHandleType method, ResourceEnum resourceEnum) {
		return new MappingJackson2HttpMessageConverter();
	}

	private <T> MultiValueMap<String, Object> getFormDateIfNeed(T resource,
			ResourceEnum resourceEnum) {
		if (resource == null)
			return null;
		MultiValueMap<String, Object> formDate = new LinkedMultiValueMap<String, Object>();

		// TODO:本来是想用反射来做数据的form的，不过，这里考虑到效率的问题，还是算了，而且，实际的数据
		// 是怎样的,是不是有必要,也不好说,应为form参数不会太多，大多数时候是读数据而已
		// .先直接手动做着,后面如果有需要用的话,再用发射来简化开发.设计的时候留着空间就好
		return null;
	}

	private static class NewFormHttpMessageConveter extends
			FormHttpMessageConverter {

	}
}
